// 
// SoapTrace.Net - A soap message monitoring utility. 
// 
// Copyright (C) 2004 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using NUnit.Framework;
using SoapTrace.Core.Communication;
using SoapTrace.Tests.Utils;
using System;

namespace SoapTrace.Tests
{
	/// <summary>
	/// Tests the <see cref="SoapPacketMonitor"/> class parsing
	/// of network data packets.  The test fixture creates a test 
	/// <see cref="DerivedSoapPacketMonitor"/> object that 
	/// derives from <see cref="SoapPacketMonitor"/>.  This
	/// derived class allows us to send test data packets to the 
	/// <see cref="SoapPacketMonitor"/> directly via 
	/// <see cref="DerivedSoapPacketMonitor.ProcessPacket"/> bypassing
	/// the winpcap libraries.  The main reason for this, apart from
	/// gaining total control of the data fed into the 
	/// <see cref="SoapPacketMonitor"/>, is that winpcap cannot capture
	/// packets sent to the local machine since the packets do not go all the
	/// way through the network layers.
	/// </summary>
	[ TestFixture ]
	public class SoapPacketMonitorTestFixture
	{
		/// <summary>
		/// The derived packet monitor object that we use to 
		/// test the <see cref="SoapPacketMonitor"/> class.
		/// </summary>
		private DerivedSoapPacketMonitor m_PacketMonitor;
		
		/// <summary>
		/// The remote server's IP address.
		/// </summary>
		private string m_RemoteServerIPAddress = "200.1.1.1";
		
		/// <summary>
		/// The remote server's port.
		/// </summary>
		private int m_RemoteServerPort = 80;
		
		/// <summary>
		/// The network interface name.
		/// </summary>
		private string m_NetworkInterfaceName = 
			@"\Device\NPF_{AAB3AD71-8470-4B6E-AF6B-9B841AEE3059}";
		
		/// <summary>
		/// The tcp data packets.
		/// </summary>
		private string[ ] m_TcpPackets;
		
		/// <summary>
		/// The index of the soap request in the <see cref="m_TcpPackets"/> 
		/// array.
		/// </summary>
		private int m_SoapRequestPacketIndex;
				
		/// <summary>
		/// The index of the soap response in the <see cref="m_TcpPackets"/> 
		/// array.
		/// </summary>
		private int m_SoapResponsePacketIndex;
		
		/// <summary>
		/// The soap request received from the packet monitor.
		/// </summary>
		private SoapRequest m_SoapRequest;
		
		/// <summary>
		/// The soap response received from the packet monitor.
		/// </summary>
		private SoapResponse m_SoapResponse;
		
		/// <summary>
		/// Initialises each test.  Creates a 
		/// <see cref="DerivedSoapPacketMonitor"/> object to test.
		/// </summary>
		[ SetUp ]
		public void Init( )
		{
			m_PacketMonitor = new DerivedSoapPacketMonitor( );
			m_PacketMonitor.Start( m_NetworkInterfaceName,
				m_RemoteServerIPAddress,
				m_RemoteServerPort );
			
			m_PacketMonitor.SoapResponseMessageReceived += 
				new SoapResponseMessageReceivedEventHandler( OnSoapResponseReceived );
			
			m_PacketMonitor.SoapRequestMessageReceived += 
				new SoapRequestMessageReceivedEventHandler( OnSoapRequestReceived );
			
			CreateDataPackets( );
			
			SendDataPackets( );
		}
		
		/// <summary>
		/// Clears up after each test.
		/// </summary>
		[ TearDown ]
		public void TearDown( )
		{
			m_PacketMonitor.Stop( );
		}
		
		/// <summary>
		/// Tests that a soap request message arrives as expected.
		/// </summary>
		[ Test ]
		public void SoapRequestArrived( )
		{
			string ExpectedRequestMessage = 	
				"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
				"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
				"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
				"<soap:Body>" +
				"<Add xmlns=\"http://tempuri.org/\">" +
				"<value1>5</value1>" +
				"<value2>6</value2>" +
				"</Add>" +
				"</soap:Body>" +
				"</soap:Envelope>";
			
			Assert.AreEqual( ExpectedRequestMessage,
				m_SoapRequest.Message,
			    "Soap request message is wrong." );
		}
		
		/// <summary>
		/// Tests that a soap response message arrives as expected.
		/// </summary>
		[ Test ]
		public void SoapResponseArrived( )
		{
			string ExpectedResponseMessage = 
				"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
				"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
				"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
				"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
				"<soap:Body>" +
				"<AddResponse xmlns=\"http://tempuri.org/\">" +
				"<AddResult>11</AddResult>" +
				"</AddResponse>" +
				"</soap:Body>" +
				"</soap:Envelope>";
			
			Assert.AreEqual( ExpectedResponseMessage,
				m_SoapResponse.Message,
			    "Soap response message is wrong." );			
		}
		
		/// <summary>
		/// Tests that the soap request http header is parsed
		/// correctly.</summary>
		[ Test ]
		public void SoapRequestHeader( )
		{
			string ExpectedHeader = 
				"POST /MathWebService/Maths.asmx HTTP/1.1\r\n" +
				"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.1.4322.573)\r\n" +
				"Content-Type: text/xml; charset=utf-8\r\n" +
				"SOAPAction: \"http://tempuri.org/Add\"\r\n" +
				"Content-Length: 321\r\n" +
				"Expect: 100-continue\r\n" +
				"Connection: Keep-Alive\r\n" +
				"Host: 200.1.1.1\r\n\r\n";
			
			Assert.AreEqual( ExpectedHeader,
			                m_SoapRequest.Header,
			                "Soap request header is incorrect." );
		}
		
		/// <summary>
		/// Tests that the soap response http header has been
		/// parsed correctly.
		/// </summary>
		[ Test ]
		public void SoapResponseHeader( )
		{
			string ExpectedHeader = 
				"HTTP/1.1 200 OK\r\n" + 
				"Server: Microsoft-IIS/5.0\r\n" + 
				"Date: Mon, 04 Oct 2004 17:11:00 GMT\r\n" + 
				"X-Powered-By: ASP.NET\r\n" + 
				"X-AspNet-Version: 1.1.4322\r\n" + 
				"Cache-Control: private, max-age=0\r\n" + 
				"Content-Type: text/xml; charset=utf-8\r\n" + 
				"Content-Length: 326\r\n\r\n";
			
			Assert.AreEqual( ExpectedHeader,
			                m_SoapResponse.Header,
			                "Soap response header is incorrect." );
			
		}
		
		/// <summary>
		/// Creates a string array of tcp data packets that
		/// will be used in this test fixture.  
		/// </summary>
		/// <remarks>
		/// The data packets added to the string array are a 
		/// successful soap request/response communication between
		/// a client and a server.
		/// </remarks>
		private void CreateDataPackets( )
		{
			m_TcpPackets = new string[ 9 ];
			
			// SYN       200.1.1.2 -> 200.1.1.1
			m_TcpPackets[ 0 ] = 
				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00\n" +
				"0010  00 30 00 6F 40 00 80 06  68 53 C8 01 01 02 C8 01\n" +
				"0020  01 01 04 29 00 50 3F 0F  ED 1B 00 00 00 00 70 02\n" + 
				"0030  FF FF C0 75 00 00 02 04  05 B4 01 01 04 02";

			// SYN, ACK  200.1.1.1 -> 200.1.1.2
			m_TcpPackets[ 1 ] = 
				"0000  00 90 F5 2B 8D 2F 00 40  F4 8C AA 2F 08 00 45 00\n" + 
				"0010  00 30 00 1F 40 00 80 06  68 A3 C8 01 01 01 C8 01\n" + 
				"0020  01 02 00 50 04 29 5D 4E  AB 76 3F 0F ED 1C 70 12\n" + 
				"0030  44 70 73 2F 00 00 02 04  05 B4 01 01 04 02";


			// ACK       200.1.1.2 -> 200.1.1.1
			m_TcpPackets[ 2 ] = 
				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00 \n" + 
				"0010  00 28 00 70 40 00 80 06  68 5A C8 01 01 02 C8 01 \n" + 
				"0020  01 01 04 29 00 50 3F 0F  ED 1C 5D 4E AB 77 50 10 \n" + 
				"0030  FF FF E4 63 00 00 ";

			// HTTP POST 200.1.1.2 -> 200.1.1.1  /MathWebService/Maths.asmx
			m_TcpPackets[ 3 ] = 

				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00\n" + 
				"0010  01 53 00 71 40 00 80 06  67 2E C8 01 01 02 C8 01\n" + 
				"0020  01 01 04 29 00 50 3F 0F  ED 1C 5D 4E AB 77 50 18\n" + 
				"0030  FF FF 49 30 00 00 50 4F  53 54 20 2F 4D 61 74 68 \n" +
				"0040  57 65 62 53 65 72 76 69  63 65 2F 4D 61 74 68 73 \n" +
				"0050  2E 61 73 6D 78 20 48 54  54 50 2F 31 2E 31 0D 0A \n" +
				"0060  55 73 65 72 2D 41 67 65  6E 74 3A 20 4D 6F 7A 69 \n" +
				"0070  6C 6C 61 2F 34 2E 30 20  28 63 6F 6D 70 61 74 69 \n" +
				"0080  62 6C 65 3B 20 4D 53 49  45 20 36 2E 30 3B 20 4D \n" +
				"0090  53 20 57 65 62 20 53 65  72 76 69 63 65 73 20 43 \n" +
				"00A0  6C 69 65 6E 74 20 50 72  6F 74 6F 63 6F 6C 20 31 \n" +
				"00B0  2E 31 2E 34 33 32 32 2E  35 37 33 29 0D 0A 43 6F \n" +
				"00C0  6E 74 65 6E 74 2D 54 79  70 65 3A 20 74 65 78 74 \n" +
				"00D0  2F 78 6D 6C 3B 20 63 68  61 72 73 65 74 3D 75 74 \n" +
				"00E0  66 2D 38 0D 0A 53 4F 41  50 41 63 74 69 6F 6E 3A \n" +
				"00F0  20 22 68 74 74 70 3A 2F  2F 74 65 6D 70 75 72 69 \n" +
				"0100  2E 6F 72 67 2F 41 64 64  22 0D 0A 43 6F 6E 74 65 \n" +
				"0110  6E 74 2D 4C 65 6E 67 74  68 3A 20 33 32 31 0D 0A \n" +
				"0120  45 78 70 65 63 74 3A 20  31 30 30 2D 63 6F 6E 74 \n" +
				"0130  69 6E 75 65 0D 0A 43 6F  6E 6E 65 63 74 69 6F 6E \n" +
				"0140  3A 20 4B 65 65 70 2D 41  6C 69 76 65 0D 0A 48 6F \n" +
				"0150  73 74 3A 20 32 30 30 2E  31 2E 31 2E 31 0D 0A 0D \n" +
				"0160  0A ";


			// HTTP CONT 200.1.1.1 -> 200.1.1.2 
			m_TcpPackets[ 4 ] = 

				"0000  00 90 F5 2B 8D 2F 00 40  F4 8C AA 2F 08 00 45 00 \n" +
				"0010  00 98 00 20 40 00 80 06  68 3A C8 01 01 01 C8 01 \n" +
				"0020  01 02 00 50 04 29 5D 4E  AB 77 3F 0F EE 47 50 18 \n" +
				"0030  43 45 9D 42 00 00 48 54  54 50 2F 31 2E 31 20 31 \n" +
				"0040  30 30 20 43 6F 6E 74 69  6E 75 65 0D 0A 53 65 72 \n" +
				"0050  76 65 72 3A 20 4D 69 63  72 6F 73 6F 66 74 2D 49 \n" +
				"0060  49 53 2F 35 2E 30 0D 0A  44 61 74 65 3A 20 4D 6F \n" +
				"0070  6E 2C 20 30 34 20 4F 63  74 20 32 30 30 34 20 31 \n" +
				"0080  37 3A 31 31 3A 30 30 20  47 4D 54 0D 0A 58 2D 50 \n" +
				"0090  6F 77 65 72 65 64 2D 42  79 3A 20 41 53 50 2E 4E \n" +
				"00A0  45 54 0D 0A 0D 0A ";


			// Soap Request 200.1.1.2 -> 200.1.1.1
			m_SoapRequestPacketIndex = 5;
			m_TcpPackets[ 5 ] = 

				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00 \n" +
				"0010  01 69 00 72 40 00 80 06  67 17 C8 01 01 02 C8 01 \n" +
				"0020  01 01 04 29 00 50 3F 0F  EE 47 5D 4E AB E7 50 18 \n" +
				"0030  FF 8F E5 7D 00 00 3C 3F  78 6D 6C 20 76 65 72 73 \n" +
				"0040  69 6F 6E 3D 22 31 2E 30  22 20 65 6E 63 6F 64 69 \n" +
				"0050  6E 67 3D 22 75 74 66 2D  38 22 3F 3E 3C 73 6F 61 \n" +
				"0060  70 3A 45 6E 76 65 6C 6F  70 65 20 78 6D 6C 6E 73 \n" +
				"0070  3A 73 6F 61 70 3D 22 68  74 74 70 3A 2F 2F 73 63 \n" +
				"0080  68 65 6D 61 73 2E 78 6D  6C 73 6F 61 70 2E 6F 72 \n" +
				"0090  67 2F 73 6F 61 70 2F 65  6E 76 65 6C 6F 70 65 2F \n" +
				"00A0  22 20 78 6D 6C 6E 73 3A  78 73 69 3D 22 68 74 74 \n" +
				"00B0  70 3A 2F 2F 77 77 77 2E  77 33 2E 6F 72 67 2F 32 \n" +
				"00C0  30 30 31 2F 58 4D 4C 53  63 68 65 6D 61 2D 69 6E \n" +
				"00D0  73 74 61 6E 63 65 22 20  78 6D 6C 6E 73 3A 78 73 \n" +
				"00E0  64 3D 22 68 74 74 70 3A  2F 2F 77 77 77 2E 77 33 \n" +
				"00F0  2E 6F 72 67 2F 32 30 30  31 2F 58 4D 4C 53 63 68 \n" +
				"0100  65 6D 61 22 3E 3C 73 6F  61 70 3A 42 6F 64 79 3E \n" +
				"0110  3C 41 64 64 20 78 6D 6C  6E 73 3D 22 68 74 74 70 \n" +
				"0120  3A 2F 2F 74 65 6D 70 75  72 69 2E 6F 72 67 2F 22 \n" +
				"0130  3E 3C 76 61 6C 75 65 31  3E 35 3C 2F 76 61 6C 75 \n" +
				"0140  65 31 3E 3C 76 61 6C 75  65 32 3E 36 3C 2F 76 61 \n" +
				"0150  6C 75 65 32 3E 3C 2F 41  64 64 3E 3C 2F 73 6F 61 \n" +
				"0160  70 3A 42 6F 64 79 3E 3C  2F 73 6F 61 70 3A 45 6E \n" +
				"0170  76 65 6C 6F 70 65 3E ";

			// SOAP response 200.1.1.2 -> 200.1.1.1
			m_SoapResponsePacketIndex = 6;
			m_TcpPackets[ 6 ] = 

				"0000  00 90 F5 2B 8D 2F 00 40  F4 8C AA 2F 08 00 45 00 \n" +
				"0010  02 53 00 21 40 00 80 06  66 7E C8 01 01 01 C8 01 \n" +
				"0020  01 02 00 50 04 29 5D 4E  AB E7 3F 0F EF 88 50 18 \n" +
				"0030  42 04 AF 3E 00 00 48 54  54 50 2F 31 2E 31 20 32 \n" +
				"0040  30 30 20 4F 4B 0D 0A 53  65 72 76 65 72 3A 20 4D \n" +
				"0050  69 63 72 6F 73 6F 66 74  2D 49 49 53 2F 35 2E 30 \n" +
				"0060  0D 0A 44 61 74 65 3A 20  4D 6F 6E 2C 20 30 34 20 \n" +
				"0070  4F 63 74 20 32 30 30 34  20 31 37 3A 31 31 3A 30 \n" +
				"0080  30 20 47 4D 54 0D 0A 58  2D 50 6F 77 65 72 65 64 \n" +
				"0090  2D 42 79 3A 20 41 53 50  2E 4E 45 54 0D 0A 58 2D \n" +
				"00A0  41 73 70 4E 65 74 2D 56  65 72 73 69 6F 6E 3A 20 \n" +
				"00B0  31 2E 31 2E 34 33 32 32  0D 0A 43 61 63 68 65 2D \n" +
				"00C0  43 6F 6E 74 72 6F 6C 3A  20 70 72 69 76 61 74 65 \n" +
				"00D0  2C 20 6D 61 78 2D 61 67  65 3D 30 0D 0A 43 6F 6E \n" +
				"00E0  74 65 6E 74 2D 54 79 70  65 3A 20 74 65 78 74 2F \n" +
				"00F0  78 6D 6C 3B 20 63 68 61  72 73 65 74 3D 75 74 66 \n" +
				"0100  2D 38 0D 0A 43 6F 6E 74  65 6E 74 2D 4C 65 6E 67 \n" +
				"0110  74 68 3A 20 33 32 36 0D  0A 0D 0A 3C 3F 78 6D 6C \n" +
				"0120  20 76 65 72 73 69 6F 6E  3D 22 31 2E 30 22 20 65 \n" +
				"0130  6E 63 6F 64 69 6E 67 3D  22 75 74 66 2D 38 22 3F \n" +
				"0140  3E 3C 73 6F 61 70 3A 45  6E 76 65 6C 6F 70 65 20 \n" +
				"0150  78 6D 6C 6E 73 3A 73 6F  61 70 3D 22 68 74 74 70 \n" +
				"0160  3A 2F 2F 73 63 68 65 6D  61 73 2E 78 6D 6C 73 6F \n" +
				"0170  61 70 2E 6F 72 67 2F 73  6F 61 70 2F 65 6E 76 65 \n" +
				"0180  6C 6F 70 65 2F 22 20 78  6D 6C 6E 73 3A 78 73 69 \n" +
				"0190  3D 22 68 74 74 70 3A 2F  2F 77 77 77 2E 77 33 2E \n" +
				"01A0  6F 72 67 2F 32 30 30 31  2F 58 4D 4C 53 63 68 65 \n" +
				"01B0  6D 61 2D 69 6E 73 74 61  6E 63 65 22 20 78 6D 6C \n" +
				"01C0  6E 73 3A 78 73 64 3D 22  68 74 74 70 3A 2F 2F 77 \n" +
				"01D0  77 77 2E 77 33 2E 6F 72  67 2F 32 30 30 31 2F 58 \n" +
				"01E0  4D 4C 53 63 68 65 6D 61  22 3E 3C 73 6F 61 70 3A \n" +
				"01F0  42 6F 64 79 3E 3C 41 64  64 52 65 73 70 6F 6E 73 \n" +
				"0200  65 20 78 6D 6C 6E 73 3D  22 68 74 74 70 3A 2F 2F \n" +
				"0210  74 65 6D 70 75 72 69 2E  6F 72 67 2F 22 3E 3C 41 \n" +
				"0220  64 64 52 65 73 75 6C 74  3E 31 31 3C 2F 41 64 64 \n" +
				"0230  52 65 73 75 6C 74 3E 3C  2F 41 64 64 52 65 73 70 \n" +
				"0240  6F 6E 73 65 3E 3C 2F 73  6F 61 70 3A 42 6F 64 79 \n" +
				"0250  3E 3C 2F 73 6F 61 70 3A  45 6E 76 65 6C 6F 70 65 \n" +
				"0260  3E ";

			// ACK       200.1.1.2 -> 200.1.1.1 
			m_TcpPackets[ 7 ] = 

				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00 \n" +
				"0010  00 28 00 73 40 00 80 06  68 57 C8 01 01 02 C8 01 \n" +
				"0020  01 01 04 29 00 50 3F 0F  EF 88 5D 4E AE 12 50 10 \n" +
				"0030  FD 64 E1 F7 00 00 ";

			// RST, ACK  200.1.1.2 -> 200.1.1.1  Close client app. 
			m_TcpPackets[ 8 ] = 


				"0000  00 40 F4 8C AA 2F 00 90  F5 2B 8D 2F 08 00 45 00 \n" +
				"0010  00 28 00 74 40 00 80 06  68 56 C8 01 01 02 C8 01 \n" +
				"0020  01 01 04 29 00 50 3F 0F  EF 88 5D 4E AE 12 50 14 \n" +
				"0030  00 00 DF 58 00 00 ";
	
		}
		
		/// <summary>
		/// Sends all the tcp data packets to the packet
		/// monitor object for processing.
		/// </summary>
		private void SendDataPackets( )
		{
			for( int i = 0; i < m_TcpPackets.Length; ++i )
			{
				byte[ ] Bytes = PacketBytes.GetBytesFromString( m_TcpPackets[ i ] );
				
				m_PacketMonitor.ProcessPacket( Bytes );
				
				if( i == m_SoapRequestPacketIndex )
				{
					// Check soap request has arrived.
					
					Assert.IsNotNull( m_SoapRequest, 
						"Soap request should have been processed." );
				}
				else if( i == m_SoapResponsePacketIndex )
				{
					// Check soap response has arrived.
					
					Assert.IsNotNull( m_SoapResponse, 
						"Soap response should have been processed." );				
				}
			}			
		}
		
		/// <summary>
		/// Soap request received event callback.  Saves the soap
		/// request for later testing.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapRequestReceivedEventArgs"/> object.</param>
		private void OnSoapRequestReceived( object sender, SoapRequestReceivedEventArgs e )
		{
			m_SoapRequest = e.Request;
		}
		
		/// <summary>
		/// Soap response received event callback.  Saves the soap
		/// response for later testing.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">A <see cref="SoapResponseReceivedEventArgs"/> object.</param>
		private void OnSoapResponseReceived( object sender, SoapResponseReceivedEventArgs e )
		{
			m_SoapResponse = e.Response;
		}	
	}
}
